/*-------------------------------------------------------------------------
 *
 * json.c
 *		JSON 数据类型支持。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * IDENTIFICATION
 *	  src/backend/utils/adt/json.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "catalog/pg_type.h"
#include "funcapi.h"
#include "libpq/pqformat.h"
#include "miscadmin.h"
#include "parser/parse_coerce.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/date.h"
#include "utils/datetime.h"
#include "utils/json.h"
#include "utils/jsonfuncs.h"
#include "utils/lsyscache.h"
#include "utils/typcache.h"

typedef enum					/* datum_to_json 的类型分类 */
{
	JSONTYPE_NULL,				/* null，因此我们没有费心去识别 */
	JSONTYPE_BOOL,				/* 布尔值（仅限内置类型） */
	JSONTYPE_NUMERIC,			/* 数值（同上） */
	JSONTYPE_DATE,				/* 我们对日期时间使用特殊格式 */
	JSONTYPE_TIMESTAMP,
	JSONTYPE_TIMESTAMPTZ,
	JSONTYPE_JSON,				/* JSON本身（和JSONB） */
	JSONTYPE_ARRAY,				/* array */
	JSONTYPE_COMPOSITE,			/* 复合类型 */
	JSONTYPE_CAST,				/* 显式转换为JSON的某种东西 */
	JSONTYPE_OTHER				/* all else */
} JsonTypeCategory;

typedef struct JsonAggState
{
	StringInfo	str;
	JsonTypeCategory key_category;
	Oid			key_output_func;
	JsonTypeCategory val_category;
	Oid			val_output_func;
} JsonAggState;

static void fc_composite_to_json(Datum fc_composite, StringInfo fc_result,
							  bool fc_use_line_feeds);
static void fc_array_dim_to_json(StringInfo fc_result, int fc_dim, int fc_ndims, int *fc_dims,
							  Datum *fc_vals, bool *fc_nulls, int *fc_valcount,
							  JsonTypeCategory fc_tcategory, Oid fc_outfuncoid,
							  bool fc_use_line_feeds);
static void fc_array_to_json_internal(Datum fc_array, StringInfo fc_result,
								   bool fc_use_line_feeds);
static void fc_json_categorize_type(Oid fc_typoid,
								 JsonTypeCategory *fc_tcategory,
								 Oid *fc_outfuncoid);
static void fc_datum_to_json(Datum fc_val, bool fc_is_null, StringInfo fc_result,
						  JsonTypeCategory fc_tcategory, Oid fc_outfuncoid,
						  bool fc_key_scalar);
static void add_json(Datum fc_val, bool fc_is_null, StringInfo fc_result,
					 Oid fc_val_type, bool fc_key_scalar);
static text *fc_catenate_stringinfo_string(StringInfo fc_buffer, const char *fc_addon);

/* 
 * 输入。
 */
Datum json_in(PG_FUNCTION_ARGS)
{
	char	   *fc_json = PG_GETARG_CSTRING(0);
	text	   *fc_result = cstring_to_text(fc_json);
	JsonLexContext *fc_lex;

	/* 验证它 */
	fc_lex = makeJsonLexContext(fc_result, false);
	pg_parse_json_or_ereport(fc_lex, &nullSemAction);

	/* 目前内部表示与文本相同 */
	PG_RETURN_TEXT_P(fc_result);
}

/* 
 * 输出。
 */
Datum json_out(PG_FUNCTION_ARGS)
{
	/* 我们无需解除解压，因为text_to_cstring将处理这件事 */
	Datum		fc_txt = PG_GETARG_DATUM(0);

	PG_RETURN_CSTRING(TextDatumGetCString(fc_txt));
}

/* 
 * 二进制发送。
 */
Datum json_send(PG_FUNCTION_ARGS)
{
	text	   *fc_t = PG_GETARG_TEXT_PP(0);
	StringInfoData fc_buf;

	pq_begintypsend(&fc_buf);
	pq_sendtext(&fc_buf, VARDATA_ANY(fc_t), VARSIZE_ANY_EXHDR(fc_t));
	PG_RETURN_BYTEA_P(pq_endtypsend(&fc_buf));
}

/* 
 * 二进制接收。
 */
Datum json_recv(PG_FUNCTION_ARGS)
{
	StringInfo	fc_buf = (StringInfo) PG_GETARG_POINTER(0);
	char	   *fc_str;
	int			fc_nbytes;
	JsonLexContext *fc_lex;

	fc_str = pq_getmsgtext(fc_buf, fc_buf->len - fc_buf->cursor, &fc_nbytes);

	/* 验证它。 */
	fc_lex = makeJsonLexContextCstringLen(fc_str, fc_nbytes, GetDatabaseEncoding(), false);
	pg_parse_json_or_ereport(fc_lex, &nullSemAction);

	PG_RETURN_TEXT_P(cstring_to_text_with_len(fc_str, fc_nbytes));
}

/* 
 * 确定我们希望在datum_to_json中如何打印给定类型的值。
 * 
 * 给定数据类型OID，返回其JsonTypeCategory，以及类型的
 * 输出函数OID。如果返回的类别是JSONTYPE_CAST，我们
 * 返回类型->JSON转换函数的OID。
 */
static void fc_json_categorize_type(Oid fc_typoid,
					 JsonTypeCategory *fc_tcategory,
					 Oid *fc_outfuncoid)
{
	bool		fc_typisvarlena;

	/* 查看任何域 */
	fc_typoid = getBaseType(fc_typoid);

	*fc_outfuncoid = InvalidOid;

	/* 
	 * 除日期和时间戳类型、数组和复合类型、布尔值以及没有
	 * 转换为JSON的非内置类型外，我们需要获取所有内容的输出函数。
	 */

	switch (fc_typoid)
	{
		case BOOLOID:
			*fc_tcategory = JSONTYPE_BOOL;
			break;

		case INT2OID:
		case INT4OID:
		case INT8OID:
		case FLOAT4OID:
		case FLOAT8OID:
		case NUMERICOID:
			getTypeOutputInfo(fc_typoid, fc_outfuncoid, &fc_typisvarlena);
			*fc_tcategory = JSONTYPE_NUMERIC;
			break;

		case DATEOID:
			*fc_tcategory = JSONTYPE_DATE;
			break;

		case TIMESTAMPOID:
			*fc_tcategory = JSONTYPE_TIMESTAMP;
			break;

		case TIMESTAMPTZOID:
			*fc_tcategory = JSONTYPE_TIMESTAMPTZ;
			break;

		case JSONOID:
		case JSONBOID:
			getTypeOutputInfo(fc_typoid, fc_outfuncoid, &fc_typisvarlena);
			*fc_tcategory = JSONTYPE_JSON;
			break;

		default:
			/* 检查数组和复合类型 */
			if (OidIsValid(get_element_type(fc_typoid)) || fc_typoid == ANYARRAYOID
				|| fc_typoid == ANYCOMPATIBLEARRAYOID || fc_typoid == RECORDARRAYOID)
				*fc_tcategory = JSONTYPE_ARRAY;
			else if (type_is_rowtype(fc_typoid))	/* 包括RECORDOID */
				*fc_tcategory = JSONTYPE_COMPOSITE;
			else
			{
				/* 这可能是一般情况... */
				*fc_tcategory = JSONTYPE_OTHER;
				/* 但如果它不是内置的，我们还是来查找转换为JSON的情况 */
				if (fc_typoid >= FirstNormalObjectId)
				{
					Oid			fc_castfunc;
					CoercionPathType fc_ctype;

					fc_ctype = find_coercion_pathway(JSONOID, fc_typoid,
												  COERCION_EXPLICIT,
												  &fc_castfunc);
					if (fc_ctype == COERCION_PATH_FUNC && OidIsValid(fc_castfunc))
					{
						*fc_tcategory = JSONTYPE_CAST;
						*fc_outfuncoid = fc_castfunc;
					}
					else
					{
						/* 没有转换的非内置类型 */
						getTypeOutputInfo(fc_typoid, fc_outfuncoid, &fc_typisvarlena);
					}
				}
				else
				{
					/* 任何其他内置类型 */
					getTypeOutputInfo(fc_typoid, fc_outfuncoid, &fc_typisvarlena);
				}
			}
			break;
	}
}

/* 
 * 将Datum转换为JSON文本，将字符串附加到“result”中。
 * 
 * tcategory和outfuncoid来自之前对json_categorize_type的调用，
 * 但如果is_null为true，则它们可能无效。
 * 
 * 如果key_scalar为true，则值作为键打印，因此需要确保
 * 它是可接受的类型，并强制其加引号。
 */
static void fc_datum_to_json(Datum fc_val, bool fc_is_null, StringInfo fc_result,
			  JsonTypeCategory fc_tcategory, Oid fc_outfuncoid,
			  bool fc_key_scalar)
{
	char	   *fc_outputstr;
	text	   *fc_jsontext;

	check_stack_depth();

	/* 调用者应确保不传入null键 */
	Assert(!(fc_key_scalar && fc_is_null));

	if (fc_is_null)
	{
		appendStringInfoString(fc_result, "null");
		return;
	}

	if (fc_key_scalar &&
		(fc_tcategory == JSONTYPE_ARRAY ||
		 fc_tcategory == JSONTYPE_COMPOSITE ||
		 fc_tcategory == JSONTYPE_JSON ||
		 fc_tcategory == JSONTYPE_CAST))
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("key value must be scalar, not array, composite, or json")));

	switch (fc_tcategory)
	{
		case JSONTYPE_ARRAY:
			fc_array_to_json_internal(fc_val, fc_result, false);
			break;
		case JSONTYPE_COMPOSITE:
			fc_composite_to_json(fc_val, fc_result, false);
			break;
		case JSONTYPE_BOOL:
			fc_outputstr = DatumGetBool(fc_val) ? "true" : "false";
			if (fc_key_scalar)
				escape_json(fc_result, fc_outputstr);
			else
				appendStringInfoString(fc_result, fc_outputstr);
			break;
		case JSONTYPE_NUMERIC:
			fc_outputstr = OidOutputFunctionCall(fc_outfuncoid, fc_val);

			/* 
			 * 如果它是有效的JSON数字，则对于非键不要调用escape_json。
			 */
			if (!fc_key_scalar && IsValidJsonNumber(fc_outputstr, strlen(fc_outputstr)))
				appendStringInfoString(fc_result, fc_outputstr);
			else
				escape_json(fc_result, fc_outputstr);
			pfree(fc_outputstr);
			break;
		case JSONTYPE_DATE:
			{
				char		fc_buf[MAXDATELEN + 1];

				JsonEncodeDateTime(fc_buf, fc_val, DATEOID, NULL);
				appendStringInfo(fc_result, "\"%s\"", fc_buf);
			}
			break;
		case JSONTYPE_TIMESTAMP:
			{
				char		fc_buf[MAXDATELEN + 1];

				JsonEncodeDateTime(fc_buf, fc_val, TIMESTAMPOID, NULL);
				appendStringInfo(fc_result, "\"%s\"", fc_buf);
			}
			break;
		case JSONTYPE_TIMESTAMPTZ:
			{
				char		fc_buf[MAXDATELEN + 1];

				JsonEncodeDateTime(fc_buf, fc_val, TIMESTAMPTZOID, NULL);
				appendStringInfo(fc_result, "\"%s\"", fc_buf);
			}
			break;
		case JSONTYPE_JSON:
			/* JSON和JSONB输出将已经被转义 */
			fc_outputstr = OidOutputFunctionCall(fc_outfuncoid, fc_val);
			appendStringInfoString(fc_result, fc_outputstr);
			pfree(fc_outputstr);
			break;
		case JSONTYPE_CAST:
			/* outfuncoid指的是转换函数，而不是输出函数 */
			fc_jsontext = DatumGetTextPP(OidFunctionCall1(fc_outfuncoid, fc_val));
			fc_outputstr = text_to_cstring(fc_jsontext);
			appendStringInfoString(fc_result, fc_outputstr);
			pfree(fc_outputstr);
			pfree(fc_jsontext);
			break;
		default:
			fc_outputstr = OidOutputFunctionCall(fc_outfuncoid, fc_val);
			escape_json(fc_result, fc_outputstr);
			pfree(fc_outputstr);
			break;
	}
}

/*
 * 将 datetime 类型 'typid' 的 'value' 编码为 ISO 格式的 JSON 字符串，
 * 使用可选的预分配缓冲区 'buf'。可选的 'tzp' 确定我们希望显示 
 * timestamptz 的时区偏移（以秒为单位）。
 */
char * JsonEncodeDateTime(char *fc_buf, Datum fc_value, Oid fc_typid, const int *fc_tzp)
{
	if (!fc_buf)
		fc_buf = palloc(MAXDATELEN + 1);

	switch (fc_typid)
	{
		case DATEOID:
			{
				DateADT		fc_date;
				struct pg_tm fc_tm;

				fc_date = DatumGetDateADT(fc_value);

				/* 与 date_out() 相同，但强制 DateStyle */
				if (DATE_NOT_FINITE(fc_date))
					EncodeSpecialDate(fc_date, fc_buf);
				else
				{
					j2date(fc_date + POSTGRES_EPOCH_JDATE,
						   &(fc_tm.tm_year), &(fc_tm.tm_mon), &(fc_tm.tm_mday));
					EncodeDateOnly(&fc_tm, USE_XSD_DATES, fc_buf);
				}
			}
			break;
		case TIMEOID:
			{
				TimeADT		fc_time = DatumGetTimeADT(fc_value);
				struct pg_tm fc_tt,
						   *fc_tm = &fc_tt;
				fsec_t		fc_fsec;

				/* 与 time_out() 相同，但强制 DateStyle */
				time2tm(fc_time, fc_tm, &fc_fsec);
				EncodeTimeOnly(fc_tm, fc_fsec, false, 0, USE_XSD_DATES, fc_buf);
			}
			break;
		case TIMETZOID:
			{
				TimeTzADT  *fc_time = DatumGetTimeTzADTP(fc_value);
				struct pg_tm fc_tt,
						   *fc_tm = &fc_tt;
				fsec_t		fc_fsec;
				int			fc_tz;

				/* 与 timetz_out() 相同，但强制 DateStyle */
				timetz2tm(fc_time, fc_tm, &fc_fsec, &fc_tz);
				EncodeTimeOnly(fc_tm, fc_fsec, true, fc_tz, USE_XSD_DATES, fc_buf);
			}
			break;
		case TIMESTAMPOID:
			{
				Timestamp	fc_timestamp;
				struct pg_tm fc_tm;
				fsec_t		fc_fsec;

				fc_timestamp = DatumGetTimestamp(fc_value);
				/* 与 timestamp_out() 相同，但强制 DateStyle */
				if (TIMESTAMP_NOT_FINITE(fc_timestamp))
					EncodeSpecialTimestamp(fc_timestamp, fc_buf);
				else if (timestamp2tm(fc_timestamp, NULL, &fc_tm, &fc_fsec, NULL, NULL) == 0)
					EncodeDateTime(&fc_tm, fc_fsec, false, 0, NULL, USE_XSD_DATES, fc_buf);
				else
					ereport(ERROR,
							(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
							 errmsg("timestamp out of range")));
			}
			break;
		case TIMESTAMPTZOID:
			{
				TimestampTz fc_timestamp;
				struct pg_tm fc_tm;
				int			fc_tz;
				fsec_t		fc_fsec;
				const char *fc_tzn = NULL;

				fc_timestamp = DatumGetTimestampTz(fc_value);

				/*
				 * 如果指定了时区，我们应用时区偏移，
				 * 将 timestamptz 转换为 pg_tm，就像它没有时区一样，
				 * 然后使用指定的时区将时间戳转换为字符串。
				 */
				if (fc_tzp)
				{
					fc_tz = *fc_tzp;
					fc_timestamp -= (TimestampTz) fc_tz * USECS_PER_SEC;
				}

				/* 与 timestamptz_out() 相同，但强制 DateStyle */
				if (TIMESTAMP_NOT_FINITE(fc_timestamp))
					EncodeSpecialTimestamp(fc_timestamp, fc_buf);
				else if (timestamp2tm(fc_timestamp, fc_tzp ? NULL : &fc_tz, &fc_tm, &fc_fsec,
									  fc_tzp ? NULL : &fc_tzn, NULL) == 0)
				{
					if (fc_tzp)
						fc_tm.tm_isdst = 1;	/* 设置时区存在标志 */

					EncodeDateTime(&fc_tm, fc_fsec, true, fc_tz, fc_tzn, USE_XSD_DATES, fc_buf);
				}
				else
					ereport(ERROR,
							(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
							 errmsg("timestamp out of range")));
			}
			break;
		default:
			elog(ERROR, "unknown jsonb value datetime type oid %u", fc_typid);
			return NULL;
	}

	return fc_buf;
}

/*
 * 处理数组的单个维度。
 * 如果是最内层维度，输出值；否则递归调用
 * 我们自己来处理下一个维度。
 */
static void fc_array_dim_to_json(StringInfo fc_result, int fc_dim, int fc_ndims, int *fc_dims, Datum *fc_vals,
				  bool *fc_nulls, int *fc_valcount, JsonTypeCategory fc_tcategory,
				  Oid fc_outfuncoid, bool fc_use_line_feeds)
{
	int			fc_i;
	const char *fc_sep;

	Assert(fc_dim < fc_ndims);

	fc_sep = fc_use_line_feeds ? ",\n " : ",";

	appendStringInfoChar(fc_result, '[');

	for (fc_i = 1; fc_i <= fc_dims[fc_dim]; fc_i++)
	{
		if (fc_i > 1)
			appendStringInfoString(fc_result, fc_sep);

		if (fc_dim + 1 == fc_ndims)
		{
			fc_datum_to_json(fc_vals[*fc_valcount], fc_nulls[*fc_valcount], fc_result, fc_tcategory,
						  fc_outfuncoid, false);
			(*fc_valcount)++;
		}
		else
		{
			/*
			 * 我们是否想在数组的内层维度上换行？暂时
			 * 我们会说不。
			 */
			fc_array_dim_to_json(fc_result, fc_dim + 1, fc_ndims, fc_dims, fc_vals, fc_nulls,
							  fc_valcount, fc_tcategory, fc_outfuncoid, false);
		}
	}

	appendStringInfoChar(fc_result, ']');
}

/*
 * 将数组转换为 JSON。
 */
static void fc_array_to_json_internal(Datum fc_array, StringInfo fc_result, bool fc_use_line_feeds)
{
	ArrayType  *fc_v = DatumGetArrayTypeP(fc_array);
	Oid			fc_element_type = ARR_ELEMTYPE(fc_v);
	int		   *fc_dim;
	int			fc_ndim;
	int			fc_nitems;
	int			fc_count = 0;
	Datum	   *fc_elements;
	bool	   *fc_nulls;
	int16		fc_typlen;
	bool		fc_typbyval;
	char		fc_typalign;
	JsonTypeCategory fc_tcategory;
	Oid			fc_outfuncoid;

	fc_ndim = ARR_NDIM(fc_v);
	fc_dim = ARR_DIMS(fc_v);
	fc_nitems = ArrayGetNItems(fc_ndim, fc_dim);

	if (fc_nitems <= 0)
	{
		appendStringInfoString(fc_result, "[]");
		return;
	}

	get_typlenbyvalalign(fc_element_type,
						 &fc_typlen, &fc_typbyval, &fc_typalign);

	fc_json_categorize_type(fc_element_type,
						 &fc_tcategory, &fc_outfuncoid);

	deconstruct_array(fc_v, fc_element_type, fc_typlen, fc_typbyval,
					  fc_typalign, &fc_elements, &fc_nulls,
					  &fc_nitems);

	fc_array_dim_to_json(fc_result, 0, fc_ndim, fc_dim, fc_elements, fc_nulls, &fc_count, fc_tcategory,
					  fc_outfuncoid, fc_use_line_feeds);

	pfree(fc_elements);
	pfree(fc_nulls);
}

/*
 * 将复合类型 / 记录转换为 JSON。
 */
static void fc_composite_to_json(Datum fc_composite, StringInfo fc_result, bool fc_use_line_feeds)
{
	HeapTupleHeader fc_td;
	Oid			fc_tupType;
	int32		fc_tupTypmod;
	TupleDesc	fc_tupdesc;
	HeapTupleData fc_tmptup,
			   *fc_tuple;
	int			fc_i;
	bool		fc_needsep = false;
	const char *fc_sep;

	fc_sep = fc_use_line_feeds ? ",\n " : ",";

	fc_td = DatumGetHeapTupleHeader(fc_composite);

	/* 提取行类型信息并找到 tupdesc */
	fc_tupType = HeapTupleHeaderGetTypeId(fc_td);
	fc_tupTypmod = HeapTupleHeaderGetTypMod(fc_td);
	fc_tupdesc = lookup_rowtype_tupdesc(fc_tupType, fc_tupTypmod);

	/* 构建一个临时 HeapTuple 控制结构 */
	fc_tmptup.t_len = HeapTupleHeaderGetDatumLength(fc_td);
	fc_tmptup.t_data = fc_td;
	fc_tuple = &fc_tmptup;

	appendStringInfoChar(fc_result, '{');

	for (fc_i = 0; fc_i < fc_tupdesc->natts; fc_i++)
	{
		Datum		fc_val;
		bool		fc_isnull;
		char	   *fc_attname;
		JsonTypeCategory fc_tcategory;
		Oid			fc_outfuncoid;
		Form_pg_attribute fc_att = TupleDescAttr(fc_tupdesc, fc_i);

		if (fc_att->attisdropped)
			continue;

		if (fc_needsep)
			appendStringInfoString(fc_result, fc_sep);
		fc_needsep = true;

		fc_attname = NameStr(fc_att->attname);
		escape_json(fc_result, fc_attname);
		appendStringInfoChar(fc_result, ':');

		fc_val = heap_getattr(fc_tuple, fc_i + 1, fc_tupdesc, &fc_isnull);

		if (fc_isnull)
		{
			fc_tcategory = JSONTYPE_NULL;
			fc_outfuncoid = InvalidOid;
		}
		else
			fc_json_categorize_type(fc_att->atttypid, &fc_tcategory, &fc_outfuncoid);

		fc_datum_to_json(fc_val, fc_isnull, fc_result, fc_tcategory, fc_outfuncoid, false);
	}

	appendStringInfoChar(fc_result, '}');
	ReleaseTupleDesc(fc_tupdesc);
}

/*
 * 将 "val" 的 JSON 文本附加到 "result"。
 *
 * 这只是 datum_to_json 的一个薄包装。如果同一类型将被
 * 多次打印，避免使用这个；最好只进行一次 json_categorize_type
 * 查找。
 */
static void add_json(Datum fc_val, bool fc_is_null, StringInfo fc_result,
		 Oid fc_val_type, bool fc_key_scalar)
{
	JsonTypeCategory fc_tcategory;
	Oid			fc_outfuncoid;

	if (fc_val_type == InvalidOid)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("could not determine input data type")));

	if (fc_is_null)
	{
		fc_tcategory = JSONTYPE_NULL;
		fc_outfuncoid = InvalidOid;
	}
	else
		fc_json_categorize_type(fc_val_type,
							 &fc_tcategory, &fc_outfuncoid);

	fc_datum_to_json(fc_val, fc_is_null, fc_result, fc_tcategory, fc_outfuncoid, fc_key_scalar);
}

/*
 * SQL 函数 array_to_json(row)
 */
Datum array_to_json(PG_FUNCTION_ARGS)
{
	Datum		fc_array = PG_GETARG_DATUM(0);
	StringInfo	fc_result;

	fc_result = makeStringInfo();

	fc_array_to_json_internal(fc_array, fc_result, false);

	PG_RETURN_TEXT_P(cstring_to_text_with_len(fc_result->data, fc_result->len));
}

/*
 * SQL 函数 array_to_json(row, prettybool)
 */
Datum array_to_json_pretty(PG_FUNCTION_ARGS)
{
	Datum		fc_array = PG_GETARG_DATUM(0);
	bool		fc_use_line_feeds = PG_GETARG_BOOL(1);
	StringInfo	fc_result;

	fc_result = makeStringInfo();

	fc_array_to_json_internal(fc_array, fc_result, fc_use_line_feeds);

	PG_RETURN_TEXT_P(cstring_to_text_with_len(fc_result->data, fc_result->len));
}

/*
 * SQL 函数 row_to_json(row)
 */
Datum row_to_json(PG_FUNCTION_ARGS)
{
	Datum		fc_array = PG_GETARG_DATUM(0);
	StringInfo	fc_result;

	fc_result = makeStringInfo();

	fc_composite_to_json(fc_array, fc_result, false);

	PG_RETURN_TEXT_P(cstring_to_text_with_len(fc_result->data, fc_result->len));
}

/*
 * SQL 函数 row_to_json(row, prettybool)
 */
Datum row_to_json_pretty(PG_FUNCTION_ARGS)
{
	Datum		fc_array = PG_GETARG_DATUM(0);
	bool		fc_use_line_feeds = PG_GETARG_BOOL(1);
	StringInfo	fc_result;

	fc_result = makeStringInfo();

	fc_composite_to_json(fc_array, fc_result, fc_use_line_feeds);

	PG_RETURN_TEXT_P(cstring_to_text_with_len(fc_result->data, fc_result->len));
}

/*
 * SQL 函数 to_json(anyvalue)
 */
Datum to_json(PG_FUNCTION_ARGS)
{
	Datum		fc_val = PG_GETARG_DATUM(0);
	Oid			fc_val_type = get_fn_expr_argtype(fcinfo->flinfo, 0);
	StringInfo	fc_result;
	JsonTypeCategory fc_tcategory;
	Oid			fc_outfuncoid;

	if (fc_val_type == InvalidOid)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("could not determine input data type")));

	fc_json_categorize_type(fc_val_type,
						 &fc_tcategory, &fc_outfuncoid);

	fc_result = makeStringInfo();

	fc_datum_to_json(fc_val, false, fc_result, fc_tcategory, fc_outfuncoid, false);

	PG_RETURN_TEXT_P(cstring_to_text_with_len(fc_result->data, fc_result->len));
}

/*
 * json_agg 转换函数
 *
 * 将输入列聚合为 json 数组值。
 */
Datum json_agg_transfn(PG_FUNCTION_ARGS)
{
	MemoryContext fc_aggcontext,
				fc_oldcontext;
	JsonAggState *fc_state;
	Datum		fc_val;

	if (!AggCheckCallContext(fcinfo, &fc_aggcontext))
	{
		/* 由于内部类型参数，无法直接调用 */
		elog(ERROR, "json_agg_transfn called in non-aggregate context");
	}

	if (PG_ARGISNULL(0))
	{
		Oid			fc_arg_type = get_fn_expr_argtype(fcinfo->flinfo, 1);

		if (fc_arg_type == InvalidOid)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("could not determine input data type")));

		/*
		 * 在一个上下文中创建这个状态对象，该上下文将在
		 * 聚合调用的持续时间内保持有效。MemoryContextSwitchTo 仅在
		 * 第一次需要，因为 StringInfo 例程确保它们
		 * 使用正确的上下文来必要时扩展对象。
		 */
		fc_oldcontext = MemoryContextSwitchTo(fc_aggcontext);
		fc_state = (JsonAggState *) palloc(sizeof(JsonAggState));
		fc_state->str = makeStringInfo();
		MemoryContextSwitchTo(fc_oldcontext);

		appendStringInfoChar(fc_state->str, '[');
		fc_json_categorize_type(fc_arg_type, &fc_state->val_category,
							 &fc_state->val_output_func);
	}
	else
	{
		fc_state = (JsonAggState *) PG_GETARG_POINTER(0);
		appendStringInfoString(fc_state->str, ", ");
	}

	/* NULL 的快速路径 */
	if (PG_ARGISNULL(1))
	{
		fc_datum_to_json((Datum) 0, true, fc_state->str, JSONTYPE_NULL,
					  InvalidOid, false);
		PG_RETURN_POINTER(fc_state);
	}

	fc_val = PG_GETARG_DATUM(1);

	/* 如果是结构类型且不是第一个项目，则添加一些空白 */
	if (!PG_ARGISNULL(0) &&
		(fc_state->val_category == JSONTYPE_ARRAY ||
		 fc_state->val_category == JSONTYPE_COMPOSITE))
	{
		appendStringInfoString(fc_state->str, "\n ");
	}

	fc_datum_to_json(fc_val, false, fc_state->str, fc_state->val_category,
				  fc_state->val_output_func, false);

	/*
	 * json_agg() 的转换类型被声明为 "internal"，这是与
	 * 指针大小相同的按值类型。因此我们可以安全地
	 * 通过 nodeAgg.c 的操作传递 JsonAggState 指针。
	 */
	PG_RETURN_POINTER(fc_state);
}

/*
 * json_agg 最终函数
 */
Datum json_agg_finalfn(PG_FUNCTION_ARGS)
{
	JsonAggState *fc_state;

	/* 由于内部类型参数，无法直接调用 */
	Assert(AggCheckCallContext(fcinfo, NULL));

	fc_state = PG_ARGISNULL(0) ?
		NULL :
		(JsonAggState *) PG_GETARG_POINTER(0);

	/* 对于没有行输入的情况返回 NULL 结果，这是聚合的标准 */
	if (fc_state == NULL)
		PG_RETURN_NULL();

	/* 否则返回状态并添加适当的数组终止符 */
	PG_RETURN_TEXT_P(fc_catenate_stringinfo_string(fc_state->str, "]"));
}

/*
 * json_object_agg 转换函数。
 *
 * 将两个输入列聚合为一个 json 对象值。
 */
Datum json_object_agg_transfn(PG_FUNCTION_ARGS)
{
	MemoryContext fc_aggcontext,
				fc_oldcontext;
	JsonAggState *fc_state;
	Datum		fc_arg;

	if (!AggCheckCallContext(fcinfo, &fc_aggcontext))
	{
		/* 由于内部类型参数，无法直接调用 */
		elog(ERROR, "json_object_agg_transfn called in non-aggregate context");
	}

	if (PG_ARGISNULL(0))
	{
		Oid			fc_arg_type;

		/*
		 * 在一个上下文中创建 StringInfo，该上下文将在
		 * 聚合调用的持续时间内保持有效。切换上下文仅在
		 * 这个初始步骤中需要，因为 StringInfo 例程确保它们
		 * 使用正确的上下文来必要时扩展对象。
		 */
		fc_oldcontext = MemoryContextSwitchTo(fc_aggcontext);
		fc_state = (JsonAggState *) palloc(sizeof(JsonAggState));
		fc_state->str = makeStringInfo();
		MemoryContextSwitchTo(fc_oldcontext);

		fc_arg_type = get_fn_expr_argtype(fcinfo->flinfo, 1);

		if (fc_arg_type == InvalidOid)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("could not determine data type for argument %d", 1)));

		fc_json_categorize_type(fc_arg_type, &fc_state->key_category,
							 &fc_state->key_output_func);

		fc_arg_type = get_fn_expr_argtype(fcinfo->flinfo, 2);

		if (fc_arg_type == InvalidOid)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("could not determine data type for argument %d", 2)));

		fc_json_categorize_type(fc_arg_type, &fc_state->val_category,
							 &fc_state->val_output_func);

		appendStringInfoString(fc_state->str, "{ ");
	}
	else
	{
		fc_state = (JsonAggState *) PG_GETARG_POINTER(0);
		appendStringInfoString(fc_state->str, ", ");
	}

	/*
	 * 注意：由于 json_object_agg() 被声明为接受 "any" 类型，
	 * 解析器不会对未知类型字面量进行任何类型转换（即，未经修饰的字符串或 NULL）。
	 * 这些值将作为类型 UNKNOWN 到达这里，幸运的是这对我们来说并不重要，
	 * 因为 unknownout() 工作正常。
	 */

	if (PG_ARGISNULL(1))
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("field name must not be null")));

	fc_arg = PG_GETARG_DATUM(1);

	fc_datum_to_json(fc_arg, false, fc_state->str, fc_state->key_category,
				  fc_state->key_output_func, true);

	appendStringInfoString(fc_state->str, " : ");

	if (PG_ARGISNULL(2))
		fc_arg = (Datum) 0;
	else
		fc_arg = PG_GETARG_DATUM(2);

	fc_datum_to_json(fc_arg, PG_ARGISNULL(2), fc_state->str, fc_state->val_category,
				  fc_state->val_output_func, false);

	PG_RETURN_POINTER(fc_state);
}

/*
 * json_object_agg 最终函数。
 */
Datum json_object_agg_finalfn(PG_FUNCTION_ARGS)
{
	JsonAggState *fc_state;

	/* 由于内部类型参数，无法直接调用 */
	Assert(AggCheckCallContext(fcinfo, NULL));

	fc_state = PG_ARGISNULL(0) ? NULL : (JsonAggState *) PG_GETARG_POINTER(0);

	/* 对于没有行输入的情况返回 NULL 结果，这是聚合的标准 */
	if (fc_state == NULL)
		PG_RETURN_NULL();

	/* 否则返回状态并添加适当的对象终止符 */
	PG_RETURN_TEXT_P(fc_catenate_stringinfo_string(fc_state->str, " }"));
}

/*
 * 聚合的辅助函数：返回给定 StringInfo 的内容加上指定的尾部字符串，作为一个文本数据项。我们需要这样做，因为聚合最终函数不允许修改聚合状态。
 */
static text * fc_catenate_stringinfo_string(StringInfo fc_buffer, const char *fc_addon)
{
	/* cstring_to_text_with_len 的自定义版本 */
	int			fc_buflen = fc_buffer->len;
	int			fc_addlen = strlen(fc_addon);
	text	   *fc_result = (text *) palloc(fc_buflen + fc_addlen + VARHDRSZ);

	SET_VARSIZE(fc_result, fc_buflen + fc_addlen + VARHDRSZ);
	memcpy(VARDATA(fc_result), fc_buffer->data, fc_buflen);
	memcpy(VARDATA(fc_result) + fc_buflen, fc_addon, fc_addlen);

	return fc_result;
}

/*
 * SQL 函数 json_build_object(变长参数 "any")
 */
Datum json_build_object(PG_FUNCTION_ARGS)
{
	int			fc_nargs;
	int			fc_i;
	const char *fc_sep = "";
	StringInfo	fc_result;
	Datum	   *fc_args;
	bool	   *fc_nulls;
	Oid		   *fc_types;

	/* 获取参数值以构建对象 */
	fc_nargs = extract_variadic_args(fcinfo, 0, false, &fc_args, &fc_types, &fc_nulls);

	if (fc_nargs < 0)
		PG_RETURN_NULL();

	if (fc_nargs % 2 != 0)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("argument list must have even number of elements"),
		/* 翻译者：%s 是 SQL 函数名称 */
				 errhint("The arguments of %s must consist of alternating keys and values.",
						 "json_build_object()")));

	fc_result = makeStringInfo();

	appendStringInfoChar(fc_result, '{');

	for (fc_i = 0; fc_i < fc_nargs; fc_i += 2)
	{
		appendStringInfoString(fc_result, fc_sep);
		fc_sep = ", ";

		/* 处理键 */
		if (fc_nulls[fc_i])
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("argument %d cannot be null", fc_i + 1),
					 errhint("Object keys should be text.")));

		add_json(fc_args[fc_i], false, fc_result, fc_types[fc_i], true);

		appendStringInfoString(fc_result, " : ");

		/* 处理值 */
		add_json(fc_args[fc_i + 1], fc_nulls[fc_i + 1], fc_result, fc_types[fc_i + 1], false);
	}

	appendStringInfoChar(fc_result, '}');

	PG_RETURN_TEXT_P(cstring_to_text_with_len(fc_result->data, fc_result->len));
}

/*
 * json_build_object 的简化情况，其中它接收 0 个参数。
 */
Datum json_build_object_noargs(PG_FUNCTION_ARGS)
{
	PG_RETURN_TEXT_P(cstring_to_text_with_len("{}", 2));
}

/*
 * SQL 函数 json_build_array(变长参数 "any")
 */
Datum json_build_array(PG_FUNCTION_ARGS)
{
	int			fc_nargs;
	int			fc_i;
	const char *fc_sep = "";
	StringInfo	fc_result;
	Datum	   *fc_args;
	bool	   *fc_nulls;
	Oid		   *fc_types;

	/* 获取参数值以构建数组 */
	fc_nargs = extract_variadic_args(fcinfo, 0, false, &fc_args, &fc_types, &fc_nulls);

	if (fc_nargs < 0)
		PG_RETURN_NULL();

	fc_result = makeStringInfo();

	appendStringInfoChar(fc_result, '[');

	for (fc_i = 0; fc_i < fc_nargs; fc_i++)
	{
		appendStringInfoString(fc_result, fc_sep);
		fc_sep = ", ";
		add_json(fc_args[fc_i], fc_nulls[fc_i], fc_result, fc_types[fc_i], false);
	}

	appendStringInfoChar(fc_result, ']');

	PG_RETURN_TEXT_P(cstring_to_text_with_len(fc_result->data, fc_result->len));
}

/*
 * json_build_array 的简化情况，其中它接收 0 个参数。
 */
Datum json_build_array_noargs(PG_FUNCTION_ARGS)
{
	PG_RETURN_TEXT_P(cstring_to_text_with_len("[]", 2));
}

/*
 * SQL 函数 json_object(text[])
 *
 * 将一维或二维文本数组作为键/值对用于 json 对象。
 */
Datum json_object(PG_FUNCTION_ARGS)
{
	ArrayType  *fc_in_array = PG_GETARG_ARRAYTYPE_P(0);
	int			fc_ndims = ARR_NDIM(fc_in_array);
	StringInfoData fc_result;
	Datum	   *fc_in_datums;
	bool	   *fc_in_nulls;
	int			fc_in_count,
				fc_count,
				fc_i;
	text	   *fc_rval;
	char	   *fc_v;

	switch (fc_ndims)
	{
		case 0:
			PG_RETURN_DATUM(CStringGetTextDatum("{}"));
			break;

		case 1:
			if ((ARR_DIMS(fc_in_array)[0]) % 2)
				ereport(ERROR,
						(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
						 errmsg("array must have even number of elements")));
			break;

		case 2:
			if ((ARR_DIMS(fc_in_array)[1]) != 2)
				ereport(ERROR,
						(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
						 errmsg("array must have two columns")));
			break;

		default:
			ereport(ERROR,
					(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
					 errmsg("wrong number of array subscripts")));
	}

	deconstruct_array(fc_in_array,
					  TEXTOID, -1, false, TYPALIGN_INT,
					  &fc_in_datums, &fc_in_nulls, &fc_in_count);

	fc_count = fc_in_count / 2;

	initStringInfo(&fc_result);

	appendStringInfoChar(&fc_result, '{');

	for (fc_i = 0; fc_i < fc_count; ++fc_i)
	{
		if (fc_in_nulls[fc_i * 2])
			ereport(ERROR,
					(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
					 errmsg("null value not allowed for object key")));

		fc_v = TextDatumGetCString(fc_in_datums[fc_i * 2]);
		if (fc_i > 0)
			appendStringInfoString(&fc_result, ", ");
		escape_json(&fc_result, fc_v);
		appendStringInfoString(&fc_result, " : ");
		pfree(fc_v);
		if (fc_in_nulls[fc_i * 2 + 1])
			appendStringInfoString(&fc_result, "null");
		else
		{
			fc_v = TextDatumGetCString(fc_in_datums[fc_i * 2 + 1]);
			escape_json(&fc_result, fc_v);
			pfree(fc_v);
		}
	}

	appendStringInfoChar(&fc_result, '}');

	pfree(fc_in_datums);
	pfree(fc_in_nulls);

	fc_rval = cstring_to_text_with_len(fc_result.data, fc_result.len);
	pfree(fc_result.data);

	PG_RETURN_TEXT_P(fc_rval);
}

/*
 * SQL 函数 json_object(text[], text[])
 *
 * 将独立的键和值文本数组对构造为 json 对象。
 */
Datum json_object_two_arg(PG_FUNCTION_ARGS)
{
	ArrayType  *fc_key_array = PG_GETARG_ARRAYTYPE_P(0);
	ArrayType  *fc_val_array = PG_GETARG_ARRAYTYPE_P(1);
	int			fc_nkdims = ARR_NDIM(fc_key_array);
	int			fc_nvdims = ARR_NDIM(fc_val_array);
	StringInfoData fc_result;
	Datum	   *fc_key_datums,
			   *fc_val_datums;
	bool	   *fc_key_nulls,
			   *fc_val_nulls;
	int			fc_key_count,
				fc_val_count,
				fc_i;
	text	   *fc_rval;
	char	   *fc_v;

	if (fc_nkdims > 1 || fc_nkdims != fc_nvdims)
		ereport(ERROR,
				(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
				 errmsg("wrong number of array subscripts")));

	if (fc_nkdims == 0)
		PG_RETURN_DATUM(CStringGetTextDatum("{}"));

	deconstruct_array(fc_key_array,
					  TEXTOID, -1, false, TYPALIGN_INT,
					  &fc_key_datums, &fc_key_nulls, &fc_key_count);

	deconstruct_array(fc_val_array,
					  TEXTOID, -1, false, TYPALIGN_INT,
					  &fc_val_datums, &fc_val_nulls, &fc_val_count);

	if (fc_key_count != fc_val_count)
		ereport(ERROR,
				(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
				 errmsg("mismatched array dimensions")));

	initStringInfo(&fc_result);

	appendStringInfoChar(&fc_result, '{');

	for (fc_i = 0; fc_i < fc_key_count; ++fc_i)
	{
		if (fc_key_nulls[fc_i])
			ereport(ERROR,
					(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
					 errmsg("null value not allowed for object key")));

		fc_v = TextDatumGetCString(fc_key_datums[fc_i]);
		if (fc_i > 0)
			appendStringInfoString(&fc_result, ", ");
		escape_json(&fc_result, fc_v);
		appendStringInfoString(&fc_result, " : ");
		pfree(fc_v);
		if (fc_val_nulls[fc_i])
			appendStringInfoString(&fc_result, "null");
		else
		{
			fc_v = TextDatumGetCString(fc_val_datums[fc_i]);
			escape_json(&fc_result, fc_v);
			pfree(fc_v);
		}
	}

	appendStringInfoChar(&fc_result, '}');

	pfree(fc_key_datums);
	pfree(fc_key_nulls);
	pfree(fc_val_datums);
	pfree(fc_val_nulls);

	fc_rval = cstring_to_text_with_len(fc_result.data, fc_result.len);
	pfree(fc_result.data);

	PG_RETURN_TEXT_P(fc_rval);
}


/*
 * 生成 JSON 字符串字面量，正确转义文本中的字符。
 */
void escape_json(StringInfo fc_buf, const char *fc_str)
{
	const char *fc_p;

	appendStringInfoCharMacro(fc_buf, '"');
	for (fc_p = fc_str; *fc_p; fc_p++)
	{
		switch (*fc_p)
		{
			case '\b':
				appendStringInfoString(fc_buf, "\\b");
				break;
			case '\f':
				appendStringInfoString(fc_buf, "\\f");
				break;
			case '\n':
				appendStringInfoString(fc_buf, "\\n");
				break;
			case '\r':
				appendStringInfoString(fc_buf, "\\r");
				break;
			case '\t':
				appendStringInfoString(fc_buf, "\\t");
				break;
			case '"':
				appendStringInfoString(fc_buf, "\\\"");
				break;
			case '\\':
				appendStringInfoString(fc_buf, "\\\\");
				break;
			default:
				if ((unsigned char) *fc_p < ' ')
					appendStringInfo(fc_buf, "\\u%04x", (int) *fc_p);
				else
					appendStringInfoCharMacro(fc_buf, *fc_p);
				break;
		}
	}
	appendStringInfoCharMacro(fc_buf, '"');
}

/*
 * SQL 函数 json_typeof(json) -> text
 *
 * 返回最外层 JSON 值的类型作为 TEXT。可能的类型有
 * "object"、"array"、"string"、"number"、"boolean" 和 "null"。
 *
 * 执行对 json_lex() 的单次调用以获取提供值的第一个标记。该初始标记唯一决定了值的类型。由于我们
 * 的输入必须已经通过 json_in() 或 json_recv() 进行了验证，初始标记绝不应为 JSON_TOKEN_OBJECT_END、JSON_TOKEN_ARRAY_END、
 * JSON_TOKEN_COLON、JSON_TOKEN_COMMA 或 JSON_TOKEN_END。
 */
Datum json_typeof(PG_FUNCTION_ARGS)
{
	text	   *fc_json;

	JsonLexContext *fc_lex;
	JsonTokenType fc_tok;
	char	   *fc_type;
	JsonParseErrorType fc_result;

	fc_json = PG_GETARG_TEXT_PP(0);
	fc_lex = makeJsonLexContext(fc_json, false);

	/* 从输入中精确提取一个标记并检查其类型。 */
	fc_result = json_lex(fc_lex);
	if (fc_result != JSON_SUCCESS)
		json_ereport_error(fc_result, fc_lex);
	fc_tok = fc_lex->token_type;
	switch (fc_tok)
	{
		case JSON_TOKEN_OBJECT_START:
			fc_type = "object";
			break;
		case JSON_TOKEN_ARRAY_START:
			fc_type = "array";
			break;
		case JSON_TOKEN_STRING:
			fc_type = "string";
			break;
		case JSON_TOKEN_NUMBER:
			fc_type = "number";
			break;
		case JSON_TOKEN_TRUE:
		case JSON_TOKEN_FALSE:
			fc_type = "boolean";
			break;
		case JSON_TOKEN_NULL:
			fc_type = "null";
			break;
		default:
			elog(ERROR, "unexpected json token: %d", fc_tok);
	}

	PG_RETURN_TEXT_P(cstring_to_text(fc_type));
}
